package com.filter;

import com.service.UserService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class MyGlobFilter implements GlobalFilter, Ordered, InitializingBean {
    ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
    private static volatile Map<String, AtomicInteger> accessTimes = new ConcurrentHashMap();
    @Value("${filter.interval}")
    private long interval;
    @Value("${filter.limitation}")
    private int limitation;
    @Autowired
    private UserService userService;
    List<String> ignoreUrl  = Arrays.asList(new String[]{
            "/api/user/register",
            "/api/user/isRegistered",
            "/api/user/login",
            "/api/code/create",
            "/api/code/validate",
            "/api/email",
    });
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //获取ip
        String clientIp = request.getRemoteAddress().getHostString();

        if(!ipLimitation(clientIp)){
            response.setStatusCode(HttpStatus.SEE_OTHER);
            return returnMsg(response,"您频繁进⾏行行注册，请求已被拒绝");
        }


        //　如果在忽略的url里，则跳过
        String path = replacePrefix(exchange.getRequest().getURI().getPath());
        String requestUrl = exchange.getRequest().getURI().getRawPath();
        if (ignore(path) || ignore(requestUrl)) {
            return chain.filter(exchange);
        }

        //获取cookie,验证token是否存在
        MultiValueMap<String, HttpCookie> cookies = request.getCookies();
        HttpCookie token = cookies.getFirst("token");
        String value = token.getValue();
        String email = userService.info(value);
        if (!StringUtils.isEmpty(email)) {
            return chain.filter(exchange);
        }

        response.setStatusCode(HttpStatus.UNAUTHORIZED); // 状态码
        return returnMsg(response,"");
    }

    public Mono<Void> returnMsg(ServerHttpResponse response,String msg){
        DataBuffer wrap = response.bufferFactory().wrap(msg.getBytes());
        return response.writeWith(Mono.just(wrap));
    }

    @Override
    public int getOrder() {
        return 0;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        executorService.scheduleAtFixedRate(() -> {
            accessTimes.clear();
        }, interval, interval, TimeUnit.MINUTES);
    }

    public boolean ipLimitation(String ip) {
        AtomicInteger atomicInteger = accessTimes.computeIfAbsent(ip, key -> {
            return new AtomicInteger();
        });
        return atomicInteger.getAndIncrement() < limitation;
    }

/**
 * 检查是否忽略url
 * @param path 路径
 * @return boolean
 */


    private boolean ignore(String path) {
        return ignoreUrl.stream()
                .anyMatch(path::startsWith);
    }

/**
 * 移除模块前缀
 * @param path 路径
 * @return String
 */


    private String replacePrefix(String path) {
        if (path.startsWith("/mate")) {
            return path.substring(path.indexOf("/",1));
        }
        return path;
    }

}
